﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace SmartRoute.Data
{
    /// <summary>
    /// A bus stop where students can be delivered to schools.
    /// </summary>
    [Serializable]
    public struct Stop
    {

        #region Fields

        /// <summary>
        /// The address of this stop.
        /// </summary>
        private string _address;

        /// <summary>
        /// The latitude of this stop
        /// </summary>
        private double _latitude;

        /// <summary>
        /// The longitude of this stop
        /// </summary>
        private double _longitude;




        /// <summary>
        /// A dictionary 
        /// </summary>
        private Dictionary<School, int> _studentsAtStop;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new stop at the given address.
        /// </summary>
        /// <param name="address">The address where this stop will be placed.</param>
        public Stop(string address, double latitude, double longitude)
        {
            _address = address;
            _latitude = latitude;
            _longitude = longitude;
            _studentsAtStop = new Dictionary<School, int>();
        }

        /// <summary>
        /// Makes a shallow copy of this stop. This is probably what you want,
        /// as you'll want changes in schools to propagate to all copies.
        /// </summary>
        /// <returns>A shallow copy of this stop object.</returns>
        public Stop Copy()
        {
            Stop output = new Stop(this.Address, this.Latitude, this.Longitude);
            foreach (var keyValPair in this._studentsAtStop)
                output.addStudentsHeadedToSchool(keyValPair.Key, keyValPair.Value);
            return output;
        }

        #endregion

        #region Accessors

        /// <summary>
        /// The address of this bus stop.
        /// </summary>
        public string Address
        {
            get { return _address; }
        }

        /// <summary>
        /// The accessor for the longitude of this stop
        /// </summary>
        public double Longitude
        {
            get { return _longitude; }
            set { _longitude = value; }
        }


        /// <summary>
        /// The accessor for the latitude of this stop
        /// </summary>
        public double Latitude
        {
            get { return _latitude; }
            set { _latitude = value; }
        }

        /// <summary>
        /// The amount of students at this stop needing to go to the given school.
        /// </summary>
        /// <param_name="school">The school to check for students for.</param>
        /// <returns>The number of students headed to "school".</returns>
        public int studentsHeadedToSchool(School school)
        {
            if (_studentsAtStop.ContainsKey(school)) return _studentsAtStop[school];
            else return 0;
        }

        /// <summary>
        /// Returns the total number of students headed to the given type of school.
        /// </summary>
        /// <param_name="_schoolType">The type of school the students are headed to.</param>
        /// <returns>The number of students headed to the school.</returns>
        public int studentsHeadedToSchoolType(SchoolType schoolType)
        {
            return _studentsAtStop.Where(x => x.Key.SchoolType == schoolType)
                .Aggregate(0, ((x, y) => x + y.Value));
        }

        /// <summary>
        /// READ: DO NOT USE. FOR SERIALIZATION PURPOSES ONLY.
        /// If there is a better way than this public property, let me know.
        /// </summary>
        public Dictionary<School, int> GetDict
        {
            get { return _studentsAtStop; }
        }

        /// <summary>
        /// Returns true if this stop is empty.
        /// </summary>
        public bool IsEmpty
        {
            //Implementation note: make sure schools where no students are going are not keys.
            get { return this._studentsAtStop.Count == 0; }
        }

        /// <summary>
        /// Returns the total number of students at the stop.
        /// </summary>
        public int TotalStudents
        {
            get { return _studentsAtStop.Values.Sum(); }
        }

        #endregion

        #region Mutators

        /// <summary>
        /// Decrements the students headed from this stop to a particular school.
        /// </summary>
        /// <param name="school">The school where the students are heading.</param>
        /// <param name="students">The number of students to remove from the schools.</param>
        public void removeStudentsHeadedToSchool(School school, int students)
        {
            if (_studentsAtStop.ContainsKey(school))
            {
                if (students < _studentsAtStop[school])
                {
                    _studentsAtStop[school] = _studentsAtStop[school] - students;
                }
                else _studentsAtStop.Remove(school);
            }
        }

        /// <summary>
        /// Increments the students headed from this stop to a particular school.
        /// </summary>
        /// <param name="school">The school to which these students will be headed.</param>
        /// <param name="students">The number of students to add to the school.</param>
        public void addStudentsHeadedToSchool(School school, int students)
        {
            if (_studentsAtStop.ContainsKey(school))
            {
                _studentsAtStop[school] = _studentsAtStop[school] + students;
            }
            else _studentsAtStop[school] = students;
        }

        /// <summary>
        /// Sets the number of students headed to the given school to 0.
        /// </summary>
        /// <param name="school">The school for which all students are to be removed.</param>
        public void removeAllStudentsHeadedToSchool(School school)
        {
            _studentsAtStop.Remove(school);
        }

        #endregion

    }
}
